//
//  STDSCloneStampEffect.h
//  STDSRenderSDKIOS
//
//  Created by 陈学明 on 2023/10/9.
//

#ifndef STDSCloneStampEffect_h
#define STDSCloneStampEffect_h
#include "STDSBaseEffect.hpp"
// 仿制图章工具

class STDSCloneStampEffect: public STDSBaseEffect {
public:
    STDSCloneStampEffect() {
        mEffectType = STDSEffectTypeCloneStamp;
    }
    ~STDSCloneStampEffect() {
//        printf("dealloc--STDSMosaicEffect\n");
    }
    
    void initGL() override {
        STDSBaseEffect::initGL();
    }
    
    void setFloatArguments(const char *name, float *floatValue, int n) override {
        // B移动的距离
        if (strcmp(name, "displacement_AB") == 0) {
            float displacementX = *floatValue;
            float displacementY = *(floatValue+1);
            mDisplacement_AB = glm::vec2(displacementX/mSizeSRC.x, -displacementY/mSizeSRC.y);
            printf("\n displacement_AB x: %.5f, y: %.5f \n", mDisplacement_AB.x, mDisplacement_AB.y);
            // C移动的距离
        } else if (strcmp(name, "displacement_AC") == 0) {
            float displacementX = *floatValue;
            float displacementY = *(floatValue+1);
            mDisplacement_AC = glm::vec2(displacementX/mSizeSRC.x, -displacementY/mSizeSRC.y);
            printf("\n displacement_AC x: %.5f, y: %.5f \n", mDisplacement_AC.x, mDisplacement_AC.y);
            // mask 中心
        } else if (strcmp(name, "mask_center") == 0) {
            mMaskCenter = glm::vec2(*floatValue, 1.0 - *(floatValue+1));
            printf("\n mMaskCenter x: %.5f, y: %.5f \n", mMaskCenter.x, mMaskCenter.y);
            // 透明度
        } else if (strcmp(name, "alpha") == 0) {
            mAlpha = *floatValue;
            // 边框
        } else if (strcmp(name, "eage_B") == 0) {
            mIsShowEageB = *floatValue;
        } else if (strcmp(name, "eage_C") == 0) {
            mIsShowEageC = *floatValue;
        } else if (strcmp(name, "scale") == 0) {
            mMaskScale = *floatValue;
        } else if (strcmp(name, "angle_C") == 0) {
            mMaskAngleC = *floatValue;
        } else if (strcmp(name, "angle_B") == 0) {
            mMaskAngleB = *floatValue;
        } else if (strcmp(name, "eage_color_B") == 0) {
            mEageColorB.x = *floatValue;
            mEageColorB.y = *(floatValue+1);
            mEageColorB.z = *(floatValue+2);
        } else if (strcmp(name, "eage_color_C") == 0) {
            mEageColorC.x = *floatValue;
            mEageColorC.y = *(floatValue+1);
            mEageColorC.z = *(floatValue+2);
        }
    }
    
    void setStringArtuments(const char *name, const char *stringValue) override {
        if (strcmp(name, "mask") == 0) {
            auto mask_path = std::string(stringValue);
            this->mContext->autoRelease(this->mMask);
            this->mContext->autoRelease(this->mCacheSmoothMask);
            this->mContext->autoRelease(this->mCacheEage);
            this->mMask = this->mContext->getImageTexture(mask_path);
            this->mCacheSmoothMask = nullptr;
            this->mCacheEage = nullptr;
        } else if (strcmp(name, "reset") == 0) {
            resetArguments();
        }
    }
    
    void setBitmapArguments(const char *name, const char *data, int width, int height) override {
        if (strcmp(name, "mask") == 0) {
            this->mContext->autoRelease(this->mMask);
            this->mContext->autoRelease(this->mCacheSmoothMask);
            this->mContext->autoRelease(this->mCacheEage);
            this->mMask = make_shared<STDSTexture>((const unsigned char *)data, width, height);
            this->mCacheSmoothMask = nullptr;
            this->mCacheEage = nullptr;
        }
    }
    
    void setIntArtuments(const char *name, int *intValue, int n) override {
        if (strcmp(name, "type") == 0) {
            this->mType = *intValue;
        } else if (strcmp(name, "feather") == 0) {
            this->mFeather = *intValue;
            this->mContext->autoRelease(this->mCacheSmoothMask);
            this->mCacheSmoothMask = nullptr;
        } else if (strcmp(name, "flip") == 0) {
            mFlip = *intValue;
        }
    }
    
public:
    shared_ptr<STDSShaderProgram> getCloneStampProgram() {
        if (mCloneStampProgram == nullptr) {
            mCloneStampProgram = mContext->getProgram(kShaderSRCCloneStamp);
        }
        return mCloneStampProgram;
    }
    
    shared_ptr<STDSShaderProgram> getBlurProgram() {
        if (mBlurProgram == nullptr) {
            mBlurProgram = mContext->getProgram(kShaderSRCBoxBlur);
        }
        return mBlurProgram;
    }
    
    shared_ptr<STDSShaderProgram> getEageProgram() {
        if (mEageProgram == nullptr) {
            mEageProgram = mContext->getProgram(kShaderSRCSobelEage);
        }
        return mEageProgram;
    }
    
    shared_ptr<STDSShaderProgram> getTransformProgram() {
        if (mTransformProgram == nullptr) {
            mTransformProgram = mContext->getProgram(kShaderSRCTransformmation);
        }
        return mTransformProgram;
    }
    // 绘制
    void draw() override {
        auto cloneStampProgram = getCloneStampProgram();
        auto blurProgram = getBlurProgram();
        auto eageProgram = getEageProgram();
        if (blurProgram == nullptr || mTexture == nullptr || cloneStampProgram== nullptr || eageProgram == nullptr) {
            return;
        }
        if (mMask == nullptr) {
            drawTexture(mTexture->getId());
            return;
        }
        
        
        
        if (mCacheSmoothMask == nullptr) {
            shared_ptr<STDSRenderTarget> rt = make_shared<STDSRenderTarget>(mMask->getWidth(), mMask->getHeight());
            rt->bindSelf();
            drawFeather(blurProgram, mMask);
            rt->restore();

            shared_ptr<STDSRenderTarget> rt1 = make_shared<STDSRenderTarget>(mMask->getWidth(), mMask->getHeight());
            rt1->bindSelf();
            drawFeather(blurProgram, rt->getTexture());
            rt1->restore();
            mCacheSmoothMask = rt1->getTexture();
        }
        
        if (mCacheEage == nullptr) {
            shared_ptr<STDSRenderTarget> rt = make_shared<STDSRenderTarget>(mMask->getWidth(), mMask->getHeight());
            rt->bindSelf();
            drawEage(eageProgram, mMask);
            rt->restore();
            mCacheEage = rt->getTexture();
        }
        drawCloneStamp(cloneStampProgram, mTexture, mCacheSmoothMask, mCacheEage);
    }
    
    // 预处理 mask
    void preprocessMask(shared_ptr<STDSShaderProgram> program, shared_ptr<STDSTexture> mask) {
        program->use();
        if (mTexture != nullptr) {
            glActiveTexture(GL_TEXTURE0);
            glBindTexture(GL_TEXTURE_2D, mTexture->getId());
            program->setUniform(program->getUniform("uTexture"), 0);
        }
        program->setUniform(program->getUniform("uAlpha"), 1.0f);
        program->setMatrix(program->getUniform("uMatVex"), glm::value_ptr(mMatV));
        program->setMatrix(program->getUniform("uMatTex"), glm::value_ptr(mMatT));
        
        mAtbRect.setPosVex(program->getAttrib("aPos"));
        mAtbRect.setPosTex(program->getAttrib("aTexCoord"));
        
        mAtbRect.draw();
        program->unuse();
    }
    
    // 绘制描边
    void drawEage(shared_ptr<STDSShaderProgram> program, shared_ptr<STDSTexture> mask) {
        program->use();
        if (mask != nullptr) {
            glActiveTexture(GL_TEXTURE0);
            glBindTexture(GL_TEXTURE_2D, mask->getId());
            program->setUniform(program->getUniform("uOriginMask"), 0);
        }
        program->setUniform1fv(program->getUniform("gx_core"), 9, mSobelCoreX.data());
        program->setUniform1fv(program->getUniform("gy_core"), 9, mSobelCoreY.data());
        program->setUniform2f(program->getUniform("uSize"), mSizeSRC.x, mSizeSRC.y);
        mAtbRect.setPosVex(program->getAttrib("aPos"));
        mAtbRect.setPosTex(program->getAttrib("aTexCoord"));
        mAtbRect.draw();
        program->unuse();
    }
    
    //  仿制图章
    void drawCloneStamp(shared_ptr<STDSShaderProgram> program, shared_ptr<STDSTexture> tex, shared_ptr<STDSTexture> mask, shared_ptr<STDSTexture> eage) {
        program->use();
        if (tex != nullptr) {
            glActiveTexture(GL_TEXTURE0);
            glBindTexture(GL_TEXTURE_2D, tex->getId());
            program->setUniform(program->getUniform("uTexture"), 0);
        }
        if (mask != nullptr) {
            glActiveTexture(GL_TEXTURE1);
            glBindTexture(GL_TEXTURE_2D, mask->getId());
            program->setUniform(program->getUniform("uMask"), 1);
        }
        
        if (eage != nullptr) {
            glActiveTexture(GL_TEXTURE2);
            glBindTexture(GL_TEXTURE_2D, eage->getId());
            program->setUniform(program->getUniform("uEage"), 2);
        }
        program->setUniform(program->getUniform("uAlpha"), mAlpha);
        program->setUniform(program->getUniform("uIsShowEageB"), mIsShowEageB);
        program->setUniform(program->getUniform("uIsShowEageC"), mIsShowEageC);
        program->setUniform2f(program->getUniform("uMaskCenter"), mMaskCenter.x, mMaskCenter.y);
        program->setUniform2f(program->getUniform("uSize"), mSizeSRC.x, mSizeSRC.y);
        
        program->setUniform(program->getUniform("uMaskScale"), float(1.0/mMaskScale));
        program->setUniform(program->getUniform("uMaskAngleC"), mMaskAngleC);
        program->setUniform(program->getUniform("uMaskAngleB"), mMaskAngleB);
        program->setUniform3f(program->getUniform("uEageColorC"), mEageColorC.x, mEageColorC.y, mEageColorC.z);
        program->setUniform3f(program->getUniform("uEageColorB"), mEageColorB.x, mEageColorB.y, mEageColorB.z);
        program->setUniform(program->getUniform("uFlip"), mFlip);
        if (mType == 0) {
            program->setUniform2f(program->getUniform("uDistence_AB"), mDisplacement_AB.x, mDisplacement_AB.y);
            program->setUniform2f(program->getUniform("uDistence_AC"), mDisplacement_AC.x, mDisplacement_AC.y);
        } else {
            program->setUniform2f(program->getUniform("uDistence_AC"), mDisplacement_AB.x, mDisplacement_AB.y);
            program->setUniform2f(program->getUniform("uDistence_AB"), mDisplacement_AC.x, mDisplacement_AC.y);
        }
        
        mAtbRect.setPosVex(program->getAttrib("aPos"));
        mAtbRect.setPosTex(program->getAttrib("aTexCoord"));
        mAtbRect.draw();
        program->unuse();
    }
    
    // 羽化边缘
    void drawFeather(shared_ptr<STDSShaderProgram> program, shared_ptr<STDSTexture> tex) {
        shared_ptr<STDSRenderTarget> rt = make_shared<STDSRenderTarget>(tex->getWidth(), tex->getHeight());
        rt->bindSelf();
        drawFeatherPass(0, program, tex);
        rt->restore();
        drawFeatherPass(1, program, rt->getTexture());
    }

    void drawFeatherPass(int orientation, shared_ptr<STDSShaderProgram> program, shared_ptr<STDSTexture> tex) {
        program->use();
        if (tex != nullptr) {
            glActiveTexture(GL_TEXTURE0);
            glBindTexture(GL_TEXTURE_2D, tex->getId());
            program->setUniform(program->getUniform("inputImageTexture"), 0);
        }
        program->setUniform(program->getUniform("step"), 2);
        program->setUniform(program->getUniform("radius"), mFeather);
        program->setUniform(program->getUniform("orientation"), orientation);
        program->setUniform2f(program->getUniform("size"), tex->getWidth(), tex->getHeight());
        mAtbRect.setPosVex(program->getAttrib("aPos"));
        mAtbRect.setPosTex(program->getAttrib("aTexCoord"));
        mAtbRect.draw();
        program->unuse();
    }
    
private:
    void resetArguments() {
        this->mContext->autoRelease(this->mMask);
        this->mContext->autoRelease(this->mCacheSmoothMask);
        this->mContext->autoRelease(this->mCacheEage);
        this->mMask = nullptr;
        this->mCacheSmoothMask = nullptr;
        this->mCacheEage = nullptr;
        mIsShowEageC = 1.0;
        mIsShowEageB = 1.0;
        mAlpha = 1.0;
        mMaskCenter.x = 0.0;
        mMaskCenter.y = 0.0;
        mDisplacement_AC.x = 0.0;
        mDisplacement_AC.y = 0.0;
        mDisplacement_AB.x = 0.0;
        mDisplacement_AB.y = 0.0;
        mFeather = 10;
        mType = 0;
        mFlip = 0;
        mMaskAngleC = 0.0;
        mMaskAngleB = 0.0;
        mMaskScale = 1.0;
        
        mEageColorB = glm::vec3(0.65, 0.56, 1.0);
        mEageColorC = glm::vec3(1.0, 1.0, 1.0);
    }

private:
    shared_ptr<STDSTexture> mCacheSmoothMask{nullptr}; // 缓存平滑后的mask，提升绘制性能
    shared_ptr<STDSTexture> mCacheEage{nullptr}; // 缓存描边，提升绘制性能
    shared_ptr<STDSTexture> mPreMask{nullptr};
    shared_ptr<STDSTexture> mMask{nullptr}; // mask
    glm::vec2 mDisplacement_AB{0.0, 0.0}; // 位置偏移
    glm::vec2 mDisplacement_AC{0.0, 0.0}; // 位置偏移
    glm::vec2 mMaskCenter{0.0, 0.0}; // mask 的中心位置
    glm::vec3 mEageColorB{0.65, 0.56, 1.0}; // 描边颜色B
    glm::vec3 mEageColorC{1.0, 1.0, 1.0}; // 描边颜色C
    float mMaskAngleC{0.0};
    float mMaskAngleB{0.0};
    int mFlip{0}; // 翻转 0 不翻转，1 翻转
    int mFeather{10};// 羽化 5-50
    float mMaskScale{1.0};// 缩放
    int mType{0}; // 0 C->B, 1 B->C
    float mAlpha{1.0};// 透明度
    float mIsShowEageB{1.0}; // 是否显示线框,可以理解成线框透明度
    float mIsShowEageC{1.0}; // 是否显示线框，可以理解成线框透明度
    vector<float> mSobelCoreX{-1.0, 0.0, 1.0, -2.0, 0.0, 2.0, -1.0, 0.0, 1.0};
    vector<float> mSobelCoreY{1.0, 2.0, 1.0, 0.0, 0.0, 0.0, -1.0, -2.0, -1.0};
    
    shared_ptr<STDSShaderProgram> mCloneStampProgram{nullptr}; // 仿制图章 着色器
    shared_ptr<STDSShaderProgram> mBlurProgram{nullptr}; // 模糊着色器
    shared_ptr<STDSShaderProgram> mEageProgram{nullptr}; // 描边着色器
    shared_ptr<STDSShaderProgram> mTransformProgram{nullptr}; // 仿射变换着色器
    
    
};


#endif /* STDSCloneStampEffect_h */
